package com.kexio.file.storage.impl;

import java.io.InputStream;
import java.time.LocalDateTime;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.UUID;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.stereotype.Component;

import com.aliyun.oss.ClientException;
import com.aliyun.oss.OSS;
import com.aliyun.oss.OSSClientBuilder;
import com.aliyun.oss.OSSException;
import com.aliyun.oss.model.CompleteMultipartUploadRequest;
import com.aliyun.oss.model.InitiateMultipartUploadRequest;
import com.aliyun.oss.model.InitiateMultipartUploadResult;
import com.aliyun.oss.model.OSSObject;
import com.aliyun.oss.model.ObjectMetadata;
import com.aliyun.oss.model.PartETag;
import com.aliyun.oss.model.UploadPartRequest;
import com.kexio.file.dto.FileChunk;
import com.kexio.file.dto.FileUploadRequest;
import com.kexio.file.dto.FileUploadResult;
import com.kexio.file.storage.StorageMetadata;
import com.kexio.file.storage.StorageStrategy;

/**
 * 阿里云OSS存储策略实现
 * 
 * @author kexio
 */
@Component
@ConditionalOnProperty(name = "kexio.file.aliyun.oss.enabled", havingValue = "true")
public class AliyunOssStorageStrategy implements StorageStrategy {

    private static final Logger log = LoggerFactory.getLogger(AliyunOssStorageStrategy.class);

    @Value("${kexio.file.aliyun.oss.endpoint}")
    private String endpoint;

    @Value("${kexio.file.aliyun.oss.accessKeyId}")
    private String accessKeyId;

    @Value("${kexio.file.aliyun.oss.accessKeySecret}")
    private String accessKeySecret;

    @Value("${kexio.file.aliyun.oss.bucketName}")
    private String bucketName;

    @Value("${kexio.file.aliyun.oss.domain:}")
    private String customDomain;

    private volatile OSS ossClient;

    /**
     * 获取OSS客户端（懒加载）
     */
    private OSS getOssClient() {
        if (ossClient == null) {
            synchronized (this) {
                if (ossClient == null) {
                    ossClient = new OSSClientBuilder().build(endpoint, accessKeyId, accessKeySecret);
                }
            }
        }
        return ossClient;
    }

    @Override
    public String getStorageType() {
        return "aliyun-oss";
    }

    @Override
    public FileUploadResult upload(FileUploadRequest request, InputStream inputStream) {
        try {
            String objectKey = generateObjectKey(request);
            OSS client = getOssClient();

            // 设置对象元数据
            ObjectMetadata metadata = new ObjectMetadata();
            metadata.setContentLength(request.getFileSize());
            if (request.getContentType() != null) {
                metadata.setContentType(request.getContentType());
            }

            // 添加自定义元数据
            metadata.addUserMetadata("original-filename", request.getOriginalFilename());
            if (request.getBusinessType() != null) {
                metadata.addUserMetadata("business-type", request.getBusinessType());
            }
            if (request.getBusinessId() != null) {
                metadata.addUserMetadata("business-id", request.getBusinessId());
            }

            // 上传文件
            client.putObject(bucketName, objectKey, inputStream, metadata);

            // 构建访问URL
            String accessUrl = buildAccessUrl(objectKey);

            log.info("阿里云OSS文件上传成功: {} -> {}", request.getOriginalFilename(), objectKey);

            FileUploadResult result = new FileUploadResult();
            result.setStorageKey(objectKey);
            result.setAccessUrl(accessUrl);
            result.setStorageType(getStorageType());
            result.setFileSize(request.getFileSize());
            result.setFileHash(request.getFileHash());

            return result;

        } catch (OSSException | ClientException e) {
            log.error("阿里云OSS文件上传失败: {}", request.getOriginalFilename(), e);
            throw new RuntimeException("阿里云OSS文件上传失败", e);
        }
    }

    @Override
    public InputStream download(String storageKey) {
        try {
            OSS client = getOssClient();
            OSSObject ossObject = client.getObject(bucketName, storageKey);
            return ossObject.getObjectContent();
        } catch (OSSException | ClientException e) {
            log.error("阿里云OSS文件下载失败: {}", storageKey, e);
            throw new RuntimeException("阿里云OSS文件下载失败", e);
        }
    }

    @Override
    public boolean delete(String storageKey) {
        try {
            OSS client = getOssClient();
            client.deleteObject(bucketName, storageKey);
            
            log.info("阿里云OSS文件删除成功: {}", storageKey);
            return true;
        } catch (OSSException | ClientException e) {
            log.error("阿里云OSS文件删除失败: {}", storageKey, e);
            return false;
        }
    }

    @Override
    public boolean exists(String storageKey) {
        try {
            OSS client = getOssClient();
            return client.doesObjectExist(bucketName, storageKey);
        } catch (OSSException | ClientException e) {
            log.error("检查阿里云OSS文件存在性失败: {}", storageKey, e);
            return false;
        }
    }

    @Override
    public String getAccessUrl(String storageKey, int expireSeconds) {
        try {
            OSS client = getOssClient();
            Date expiration = new Date(System.currentTimeMillis() + expireSeconds * 1000L);
            return client.generatePresignedUrl(bucketName, storageKey, expiration).toString();
        } catch (OSSException | ClientException e) {
            log.error("获取阿里云OSS文件访问URL失败: {}", storageKey, e);
            // 返回公共访问URL作为降级
            return buildAccessUrl(storageKey);
        }
    }

    @Override
    public String initMultipartUpload(FileUploadRequest request) {
        try {
            String objectKey = generateObjectKey(request);
            OSS client = getOssClient();

            InitiateMultipartUploadRequest initRequest = new InitiateMultipartUploadRequest(bucketName, objectKey);
            
            // 设置元数据
            ObjectMetadata metadata = new ObjectMetadata();
            if (request.getContentType() != null) {
                metadata.setContentType(request.getContentType());
            }
            metadata.addUserMetadata("original-filename", request.getOriginalFilename());
            initRequest.setObjectMetadata(metadata);

            InitiateMultipartUploadResult result = client.initiateMultipartUpload(initRequest);
            String uploadId = result.getUploadId() + ":" + objectKey; // 组合uploadId和objectKey

            log.info("初始化阿里云OSS分片上传: uploadId={}, file={}", uploadId, request.getOriginalFilename());
            return uploadId;

        } catch (OSSException | ClientException e) {
            log.error("初始化阿里云OSS分片上传失败: {}", request.getOriginalFilename(), e);
            throw new RuntimeException("初始化阿里云OSS分片上传失败", e);
        }
    }

    @Override
    public String uploadChunk(String uploadId, FileChunk chunk) {
        try {
            String[] parts = uploadId.split(":", 2);
            String actualUploadId = parts[0];
            String objectKey = parts[1];
            
            OSS client = getOssClient();

            UploadPartRequest uploadPartRequest = new UploadPartRequest();
            uploadPartRequest.setBucketName(bucketName);
            uploadPartRequest.setKey(objectKey);
            uploadPartRequest.setUploadId(actualUploadId);
            uploadPartRequest.setPartNumber(chunk.getChunkNumber());
            uploadPartRequest.setPartSize(chunk.getChunkSize());
            uploadPartRequest.setInputStream(chunk.getInputStream());

            PartETag partETag = client.uploadPart(uploadPartRequest).getPartETag();

            log.debug("阿里云OSS分片上传成功: uploadId={}, partNumber={}, etag={}", 
                     uploadId, chunk.getChunkNumber(), partETag.getETag());
            
            return partETag.getETag();

        } catch (OSSException | ClientException e) {
            log.error("阿里云OSS分片上传失败: uploadId={}, chunkNumber={}", uploadId, chunk.getChunkNumber(), e);
            throw new RuntimeException("阿里云OSS分片上传失败", e);
        }
    }

    @Override
    public FileUploadResult mergeChunks(String uploadId, List<String> chunkIds) {
        try {
            String[] parts = uploadId.split(":", 2);
            String actualUploadId = parts[0];
            String objectKey = parts[1];
            
            OSS client = getOssClient();

            // 构建PartETag列表
            List<PartETag> partETags = new java.util.ArrayList<>();
            for (int i = 0; i < chunkIds.size(); i++) {
                partETags.add(new PartETag(i + 1, chunkIds.get(i)));
            }

            // 完成分片上传
            CompleteMultipartUploadRequest completeRequest = new CompleteMultipartUploadRequest(
                bucketName, objectKey, actualUploadId, partETags);
            
            client.completeMultipartUpload(completeRequest);

            // 获取文件信息
            ObjectMetadata metadata = client.getObjectMetadata(bucketName, objectKey);

            FileUploadResult result = new FileUploadResult();
            result.setStorageKey(objectKey);
            result.setAccessUrl(buildAccessUrl(objectKey));
            result.setStorageType(getStorageType());
            result.setFileSize(metadata.getContentLength());

            log.info("阿里云OSS分片合并完成: uploadId={}, objectKey={}", uploadId, objectKey);
            return result;

        } catch (OSSException | ClientException e) {
            log.error("阿里云OSS分片合并失败: uploadId={}", uploadId, e);
            throw new RuntimeException("阿里云OSS分片合并失败", e);
        }
    }

    @Override
    public boolean abortMultipartUpload(String uploadId) {
        try {
            String[] parts = uploadId.split(":", 2);
            String actualUploadId = parts[0];
            String objectKey = parts[1];
            
            OSS client = getOssClient();
            client.abortMultipartUpload(new com.aliyun.oss.model.AbortMultipartUploadRequest(
                bucketName, objectKey, actualUploadId));
            
            log.info("取消阿里云OSS分片上传: uploadId={}", uploadId);
            return true;
        } catch (OSSException | ClientException e) {
            log.error("取消阿里云OSS分片上传失败: uploadId={}", uploadId, e);
            return false;
        }
    }

    @Override
    public boolean copy(String sourceKey, String targetKey) {
        try {
            OSS client = getOssClient();
            client.copyObject(bucketName, sourceKey, bucketName, targetKey);
            
            log.info("阿里云OSS文件复制成功: {} -> {}", sourceKey, targetKey);
            return true;
        } catch (OSSException | ClientException e) {
            log.error("阿里云OSS文件复制失败: {} -> {}", sourceKey, targetKey, e);
            return false;
        }
    }

    @Override
    public StorageMetadata getMetadata(String storageKey) {
        try {
            OSS client = getOssClient();
            ObjectMetadata ossMetadata = client.getObjectMetadata(bucketName, storageKey);

            StorageMetadata metadata = new StorageMetadata();
            metadata.setSize(ossMetadata.getContentLength());
            metadata.setContentType(ossMetadata.getContentType());
            metadata.setEtag(ossMetadata.getETag());
            metadata.setLastModified(LocalDateTime.ofInstant(
                ossMetadata.getLastModified().toInstant(), 
                java.time.ZoneOffset.systemDefault()));
            metadata.setCustomMetadata(ossMetadata.getUserMetadata());

            return metadata;

        } catch (OSSException | ClientException e) {
            log.error("获取阿里云OSS文件元信息失败: {}", storageKey, e);
            return null;
        }
    }

    /**
     * 生成对象键
     */
    private String generateObjectKey(FileUploadRequest request) {
        String datePrefix = LocalDateTime.now().format(java.time.format.DateTimeFormatter.ofPattern("yyyy/MM/dd"));
        String filename = UUID.randomUUID().toString();
        
        if (request.getOriginalFilename() != null) {
            String extension = getFileExtension(request.getOriginalFilename());
            if (extension != null) {
                filename += "." + extension;
            }
        }
        
        return datePrefix + "/" + filename;
    }

    /**
     * 构建访问URL
     */
    private String buildAccessUrl(String objectKey) {
        if (customDomain != null && !customDomain.isEmpty()) {
            return customDomain + "/" + objectKey;
        } else {
            return "https://" + bucketName + "." + endpoint.replaceFirst("https?://", "") + "/" + objectKey;
        }
    }

    /**
     * 获取文件扩展名
     */
    private String getFileExtension(String filename) {
        if (filename == null || filename.isEmpty()) {
            return null;
        }
        int dotIndex = filename.lastIndexOf('.');
        if (dotIndex > 0 && dotIndex < filename.length() - 1) {
            return filename.substring(dotIndex + 1).toLowerCase();
        }
        return null;
    }
}
